/** 当前正在收集依赖的监听器 */
export let currentWatcher: ReactiveWatcher | undefined

/** 表示一个响应式对象 */
export class ReactiveObject<T = any> {

	/** 实际的值 */
	#value: T

	/** 依赖当前对象的监听器 */
	readonly watchers = new Set<ReactiveWatcher>()

	/** 实际的值 */
	get value() {
		currentWatcher?.track(this)
		return this.#value
	}
	set value(value) {
		this.#value = value
		for (const watcher of this.watchers) {
			watcher.enqueueUpdate()
		}
	}

	/**
	 * 初始化新的响应式对象
	 * @param initialValue 初始值
	 */
	constructor(initialValue: T) {
		this.#value = initialValue
	}

}

/** 表示一个响应式对象监听器 */
export class ReactiveWatcher<T = any> {

	/**
	 * 初始化新的响应式对象
	 * @param effect 计算依赖的回调函数，当依赖更新后将重新执行
	 */
	constructor(readonly effect: (prevValue: T, watcher: ReactiveWatcher<T>) => T) {
		this.forceUpdate()
	}

	/** 当前监听器是否正在更新 */
	#updating: boolean

	/** 当前监听器的唯一键 */
	readonly key = Symbol()

	/** 最后一个执行的返回值 */
	value: T

	/** 强制重新执行当前监听器 */
	forceUpdate() {
		this.#updating = true
		const oldWatcher = currentWatcher
		currentWatcher = this
		for (const reactiveObject of this.#reactiveObjects) {
			reactiveObject[this.key] = ReactiveObjectState.prevAccessed
		}
		try {
			this.value = this.effect(this.value, this)
		} finally {
			currentWatcher = oldWatcher
			this.#updating = false
			if (this.#deferStop) {
				this.#deferStop = false
				this.stop()
			} else {
				for (const reactiveObject of this.#reactiveObjects) {
					if (reactiveObject[this.key] === ReactiveObjectState.prevAccessed) {
						this.untrack(reactiveObject)
					}
					reactiveObject[this.key] = ReactiveObjectState.none
				}
			}
		}
	}

	/** 正在监听的对象 */
	readonly #reactiveObjects: ReactiveObject[] = []

	/**
	 * 开始监听指定对象
	 * @param obj 要监听的对象
	 */
	track(obj: ReactiveObject) {
		if (this.#updating) {
			const state = obj[this.key] as ReactiveObjectState
			if (state) {
				if (state === ReactiveObjectState.prevAccessed) {
					obj[this.key] = ReactiveObjectState.nextAccessed
				}
				return
			}
		}
		if (obj.watchers.has(this)) {
			return
		}
		obj.watchers.add(this)
		this.#reactiveObjects.push(obj)
	}

	/**
	 * 取消监听指定对象
	 * @param obj 要监听的对象
	 */
	untrack(obj: ReactiveObject) {
		if (obj.watchers.delete(this)) {
			this.#reactiveObjects.splice(this.#reactiveObjects.indexOf(obj), 1)
		}
	}

	/**
	 * 停止监听所有对象
	 */
	untrackAll() {
		for (const reactiveObject of this.#reactiveObjects) {
			reactiveObject.watchers.delete(this)
		}
		this.#reactiveObjects.length = 0
	}

	/** 等待更新的队列 */
	static updateQueue: ReactiveWatcher[] = []

	/** 通知当前监听器在稍后更新 */
	enqueueUpdate() {
		if (this.#updating) {
			return
		}
		this.#updating = true
		const updateQueue = ReactiveWatcher.updateQueue
		if (!updateQueue.length) {
			requestAnimationFrame(() => {
				while (updateQueue.length) {
					const top = updateQueue.shift()!
					if (top.#updating) {
						top.forceUpdate()
					}
				}
			})
		}
		updateQueue.push(this)
	}

	/** 是否需要延时更新 */
	#deferStop: boolean

	/** 释放当前监听器后的回调函数 */
	onStop?: () => void

	/**
	 * 停止当前监听器
	 */
	stop() {
		if (this.#updating) {
			this.#deferStop = true
			return
		}
		this.untrackAll()
		this.onStop?.()
	}

}

/** 表示响应式对象的状态 */
const enum ReactiveObjectState {
	/** 无 */
	none = 0,
	/** 上一次被访问 */
	prevAccessed = 1,
	/** 本次被访问 */
	nextAccessed = 2,
}

/** 当前生效的监听器作用域 */
export let currentWatcherScope: ReactiveWatcherScope = {
	watchers: [],
	children: []
}

/** 表示一个监听器作用域 */
export interface ReactiveWatcherScope {
	/** 上级作用域 */
	parent?: ReactiveWatcherScope
	/** 子级作用域 */
	children: ReactiveWatcherScope[]
	/** 当前作用域内的所有监听器 */
	watchers: ReactiveWatcher[]
}

/** 进入一个新的监听器作用域 */
export function enterWatcherScope() {
	const newScope = {
		parent: currentWatcherScope,
		watchers: [],
		children: []
	}
	currentWatcherScope.children.push(newScope)
	return newScope
}

/** 退出当前监听器作用域 */
export function exitWatcherScope() {
	currentWatcherScope = currentWatcherScope.parent!
}

/**
 * 执行一个函数，收集函数中依赖的响应式对象，当任一个响应式对象更新后重新执行函数
 * @param effect 计算依赖的回调函数，当依赖更新后将重新执行
 */
export function watch<T = any>(effect: (prevValue: T, watcher: ReactiveWatcher<T>) => T) {
	const watcher = new ReactiveWatcher(effect)
	currentWatcherScope.watchers.push(watcher)
	return watcher
}

/** 暂时禁用的监听器 */
let untrackedWatcher: ReactiveWatcher | undefined

/**
 * 执行一个表达式，但忽略其中的依赖
 * @param obj 要执行的表达式
 */
export function untracked(obj: any) {
	if (!currentWatcher) {
		return obj()
	}
	untrackedWatcher = currentWatcher
	currentWatcher = undefined
	try {
		return obj()
	} finally {
		currentWatcher = untrackedWatcher
		untrackedWatcher = undefined
	}
}

/**
 * 执行一个表达式，并跟踪其中的依赖
 * @param obj 要执行的表达式
 */
export function tracked(obj: any) {
	if (currentWatcher) {
		return obj()
	}
	currentWatcher = untrackedWatcher
	try {
		return obj()
	} finally {
		currentWatcher = undefined
	}
}

const x = globalThis.x = new ReactiveObject(0)
globalThis.w = watch(() => {
	x.value
	console.log(untracked(() => x.value))
})
x.value++